//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//********************************************************* 

using System;
using System.Diagnostics;
using System.Threading;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using MicrosoftLogger = Microsoft.Practices.EnterpriseLibrary.Logging.Logger;

namespace Microsoft.TridentWordAddIn.Common
{
    public sealed class Logger
    {
        private const string UserIdKey = "ID";

        /// <summary>
        /// Prevents a default instance of the {Logger} class from being created.
        /// </summary>
        private Logger()
        {
        }

        #region "Debug (Verbose)"

        /// <summary>
        /// Debugs the specified message.
        /// </summary>
        /// <param name="message">The message.</param>
        public static void Debug(object message)
        {
            WriteMessage(message, TraceEventType.Verbose, null, null);
        }

        /// <summary>
        /// Debugs the specified message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="category">The category.</param>
        public static void Debug(object message, object category)
        {
            WriteMessage(message, TraceEventType.Verbose, category, null);
        }

        /// <summary>
        /// Debugs the specified message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="ex">The ex.</param>
        public static void Debug(object message, Exception ex)
        {
            WriteMessage(message, TraceEventType.Verbose, null, ex);
        }

        /// <summary>
        /// Debugs the specified message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="category">The category.</param>
        /// <param name="ex">The ex.</param>
        public static void Debug(object message, object category, Exception ex)
        {
            WriteMessage(message, TraceEventType.Verbose, category, ex);
        }

        #endregion

        #region "Info"

        /// <summary>
        /// Infoes the specified message.
        /// </summary>
        /// <param name="message">The message.</param>
        public static void Info(object message)
        {
            WriteMessage(message, TraceEventType.Information, null, null);
        }

        /// <summary>
        /// Infoes the specified message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="category">The category.</param>
        public static void Info(object message, object category)
        {
            WriteMessage(message, TraceEventType.Information, category, null);
        }

        /// <summary>
        /// Infoes the specified message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="ex">The ex.</param>
        public static void Info(object message, Exception ex)
        {
            WriteMessage(message, TraceEventType.Information, null, ex);
        }

        #endregion

        #region "Warning"

        /// <summary>
        /// Warns the specified message.
        /// </summary>
        /// <param name="message">The message.</param>
        public static void Warn(object message)
        {
            WriteMessage(message, TraceEventType.Warning, null, null);
        }

        /// <summary>
        /// Warns the specified message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="category">The category.</param>
        public static void Warn(object message, object category)
        {
            WriteMessage(message, TraceEventType.Warning, category, null);
        }

        /// <summary>
        /// Warns the specified message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="ex">The ex.</param>
        public static void Warn(object message, Exception ex)
        {
            WriteMessage(message, TraceEventType.Warning, null, ex);
        }

        /// <summary>
        /// Warns the specified message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="category">The category.</param>
        /// <param name="ex">The ex.</param>
        public static void Warn(object message, object category, Exception ex)
        {
            WriteMessage(message, TraceEventType.Warning, category, ex);
        }

        #endregion

        #region "Error"

        /// <summary>
        /// Errors the specified ex.
        /// </summary>
        /// <param name="ex">The ex.</param>
        public static void Error(Exception ex)
        {
            WriteMessage(ex.Message, TraceEventType.Error, null, ex);
        }

        /// <summary>
        /// Errors the specified message.
        /// </summary>
        /// <param name="message">The message.</param>
        public static void Error(object message)
        {
            WriteMessage(message, TraceEventType.Error, null, null);
        }

        /// <summary>
        /// Errors the specified message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="category">The category.</param>
        public static void Error(object message, object category)
        {
            WriteMessage(message, TraceEventType.Error, category, null);
        }

        /// <summary>
        /// Errors the specified message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="ex">The ex.</param>
        public static void Error(object message, Exception ex)
        {
            WriteMessage(message, TraceEventType.Error, null, ex);
        }

        /// <summary>
        /// Errors the specified message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="category">The category.</param>
        /// <param name="ex">The ex.</param>
        public static void Error(object message, object category, Exception ex)
        {
            WriteMessage(message, TraceEventType.Error, category, ex);
        }

        #endregion

        #region "Critical"

        /// <summary>
        /// Criticals the specified message.
        /// </summary>
        /// <param name="message">The message.</param>
        public static void Critical(object message)
        {
            WriteMessage(message, TraceEventType.Critical, null, null);
        }

        /// <summary>
        /// Criticals the specified message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="category">The category.</param>
        public static void Critical(object message, object category)
        {
            WriteMessage(message, TraceEventType.Critical, category, null);
        }

        /// <summary>
        /// Criticals the specified message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="ex">The ex.</param>
        public static void Critical(object message, Exception ex)
        {
            WriteMessage(message, TraceEventType.Critical, null, ex);
        }

        /// <summary>
        /// Criticals the specified message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="category">The category.</param>
        /// <param name="ex">The ex.</param>
        public static void Critical(object message, object category, Exception ex)
        {
            WriteMessage(message, TraceEventType.Critical, category, ex);
        }

        #endregion

        #region "Private Helper Methods"

        /// <summary>
        /// Writes the message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="eventType">Type of the event.</param>
        /// <param name="category">The category.</param>
        /// <param name="exception">The exception.</param>
        private static void WriteMessage(object message, TraceEventType eventType, object category, Exception exception)
        {
            try
            {
                LogEntry entry = new LogEntry();
                entry.Severity = eventType;
                //Add Message 
                if (message != null)
                {
                    entry.Message = message.ToString();
                }
                //Add category 
                if (category != null)
                {
                    entry.Categories.Add(category.ToString());
                }

                AddExtendedProperties(entry, exception);
                entry.TimeStamp = DateTime.Now;

                //Only log the entry if we have a valid user name 
                if (IsValidUser(entry))
                {
                    try
                    {
                        MicrosoftLogger.Write(entry);
                    }
                    catch (Exception)
                    {
                        //Something wrong with the Logger ..make sure we dispose it properly 
                        if (MicrosoftLogger.Writer != null)
                        {
                            MicrosoftLogger.Writer.Dispose();
                        }
                    }
                }
            }
            catch (Exception)
            {
            }
        }

        /// <summary>
        /// Adds the extended properties.
        /// </summary>
        /// <param name="entry">The entry.</param>
        /// <param name="exception">The exception.</param>
        private static void AddExtendedProperties(LogEntry entry, Exception exception)
        {
            if (entry.ExtendedProperties != null)
            {
                AddUserName(entry);
                AddExceptionDetails(entry, exception);
                AddDetailedTime(entry);
            }
        }

        /// <summary>
        /// Determines whether [is valid user] [the specified entry].
        /// </summary>
        /// <param name="entry">The entry.</param>
        /// <returns>
        /// 	<c>true</c> if [is valid user] [the specified entry]; otherwise, <c>false</c>.
        /// </returns>
        private static bool IsValidUser(LogEntry entry)
        {
            bool validUser = true;
            if (entry.ExtendedProperties != null)
            {
                object user = entry.ExtendedProperties[UserIdKey];
                if (user != null)
                {
                    if (user.ToString() == " ")
                    {
                        validUser = false;
                    }
                }
                else
                {
                    validUser = false;
                }
            }

            return validUser;
        }

        /// <summary>
        /// Adds the name of the user.
        /// </summary>
        /// <param name="entry">The entry.</param>
        private static void AddUserName(LogEntry entry)
        {
            string userName = GetUserName();
            entry.ExtendedProperties.Add(UserIdKey, userName);
        }

        /// <summary>
        /// Adds the exception details.
        /// </summary>
        /// <param name="entry">The entry.</param>
        /// <param name="exception">The exception.</param>
        private static void AddExceptionDetails(LogEntry entry, Exception exception)
        {
            const string StackTraceKey = "Exception";
            string exceptionDetails = string.Empty;
            if (exception != null)
            {
                string message = ConstructMessage(exception, "Exception Details");
                string stackTrace = ConstructStackTrace(exception);
                exceptionDetails = message + stackTrace;

                if (exception.InnerException != null)
                {
                    string innerExceptionMessage = ConstructMessage(exception.InnerException, " Inner Exception Details");
                    string innerExceptionStackTrace = ConstructStackTrace(exception.InnerException);
                    exceptionDetails += innerExceptionMessage;
                    exceptionDetails += innerExceptionStackTrace;
                }
            }

            entry.ExtendedProperties.Add(StackTraceKey, exceptionDetails);
        }

        /// <summary>
        /// Constructs the stack trace.
        /// </summary>
        /// <param name="exception">The exception.</param>
        /// <returns>string</returns>
        private static string ConstructStackTrace(Exception exception)
        {
            return Environment.NewLine + "\tStack Trace: " + exception.StackTrace + Environment.NewLine + "\t ---------------------------------------------------------------------------------------------" + Environment.NewLine;
        }

        /// <summary>
        /// Constructs the message.
        /// </summary>
        /// <param name="exception">The exception.</param>
        /// <param name="exceptionHeader">The exception header.</param>
        /// <returns>string</returns>
        private static string ConstructMessage(Exception exception, string exceptionHeader)
        {
            return Environment.NewLine + " ----------------------- " + exceptionHeader + " ----------------------------------------------------------------------" + Environment.NewLine + "\tMessage: " + exception.Message;
        }

        /// <summary>
        /// Adds the detailed time.
        /// </summary>
        /// <param name="entry">The entry.</param>
        private static void AddDetailedTime(LogEntry entry)
        {
            //Add the time 
            //select convert(datetime, '2008-02-12 05:37:46.330', 121) 
            string detailedTime = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fff");
            entry.ExtendedProperties.Add("Time", detailedTime);
        }

        /// <summary>
        /// Gets the name of the user.
        /// </summary>
        /// <returns>string</returns>
        private static string GetUserName()
        {
            string userName = " ";
            //Default it to unknown 
            try
            {
                if (Thread.CurrentPrincipal != null)
                {
                    userName = Thread.CurrentPrincipal.Identity.Name;
                }
            }
            catch (Exception)
            {
                // Some exception has occured 
            }

            return userName;
        }

        #endregion
    }
}